package com.fa.ojcodesandbox;

import com.fa.ojcodesandbox.model.ExecuteCodeRequest;
import com.fa.ojcodesandbox.model.ExecuteCodeResponse;
import com.fa.ojcodesandbox.model.ExecuteMessage;
import com.fa.ojcodesandbox.utils.ProcessUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * ClassName: JavaNativeCodeSandbox
 * Package: com.fa.ojcodesandbox
 * Description:Java 原生代码沙箱实现（直接使用模板方法）
 *
 * @Author:
 * @Create: 2024/5/2 - 16:09
 * @Version: v1.0
 */
@Slf4j
@Component
public class JavaNativeCodeSandbox extends JavaCodeSandboxTemplate{

    //安全管理类的全路径
    private static final String SECURITY_MANAGER_CLASS_PATH = "D:\\after-code\\oj-code-sandbox\\src\\main\\resources\\security";

    // 安全管理类
    private static final String SECURITY_MANAGER_CLASS_NAME = "MySecurityManager";
    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        return super.executeCode(executeCodeRequest);
    }

    @Override
    public List<ExecuteMessage> runCompileAfterFile(String compileAfterFileParentPath, List<String> inputList) {
        // 3. 执行 编译好的 .class文件，得到输出结果
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for (String inputArgs : inputList) {
            // 在执行程序时，限制资源分配。防止用户占用内存
//            String executeCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s Main %s", compileAfterFileParentPath, inputArgs);
            //直接在指令上指定安全管理器
            String executeCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s;%s -Djava.security.manager=%s Main %s", compileAfterFileParentPath, SECURITY_MANAGER_CLASS_PATH, SECURITY_MANAGER_CLASS_NAME, inputArgs);
            try {
                // 在执行用户传入的代码时，使用java安全管理器
                //System.setSecurityManager(new MySecurityManager());在这里指定安全管理器会浪费很多资源
                // Runtime.getRuntime()是开启新的子进程
                Process executeProcess = Runtime.getRuntime().exec(executeCmd);
                // 超时控制。开启新线程，防止恶意代码恶意占用程序
                new Thread(() -> {
                    try {
                        Thread.sleep(TIME_OUT);
                        // 线程睡醒了，就 判断进程是否还在执行，若还在执行则 销毁 开启的子进程
                        if (executeProcess.isAlive()) {
                            executeProcess.destroy();
                            throw new RuntimeException("超时了，中断");
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }).start();
                ExecuteMessage runExecuteMessage = ProcessUtils.getRunProcessResult(executeProcess, "执行");
                // 将每次的执行信息添加到 executeMessageList
                executeMessageList.add(runExecuteMessage);
                Long actualTime = runExecuteMessage.getTime();
                if (actualTime != null) {
                    maxRunTime = Math.max(maxRunTime, actualTime);
                }
            } catch (IOException e) {
                throw new RuntimeException("程序执行错误" + e);
            }
        }
        return executeMessageList;
    }
}
